package com.audaque.springboot.foshanupload.web.productdemo.service.impl;

import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.audaque.springboot.foshanupload.authcore.service.inface.LoginService;
import com.audaque.springboot.foshanupload.core.util.DateFormatUtil;
import com.audaque.springboot.foshanupload.core.util.DateUtilsExtLang3;
import com.audaque.springboot.foshanupload.core.util.LocalDateTimeUtils;
import com.audaque.springboot.foshanupload.mp.util.ColumnUtil;
import com.audaque.springboot.foshanupload.web.productdemo.constants.RedisBusConst;
import com.audaque.springboot.foshanupload.web.productdemo.doc.ProductEs;
import com.audaque.springboot.foshanupload.web.productdemo.entity.Product;
import com.audaque.springboot.foshanupload.web.productdemo.entity.Rule;
import com.audaque.springboot.foshanupload.web.productdemo.mapper.ProductMapper;
import com.audaque.springboot.foshanupload.web.productdemo.req.ProductReq;
import com.audaque.springboot.foshanupload.web.productdemo.service.IProductService;
import com.audaque.springboot.foshanupload.web.productdemo.service.IRuleService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * <p>
 * 商品 服务实现类
 * </p>
 *
 * @author zgb
 * @since 2024-04-21
 */

@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private LoginService loginService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IRuleService iRuleService;
    //分页分批导出最大批量
    private Integer pageExportSize = 100;
    private Integer visitCountMax = 100;

    public boolean createIndexAndPushMapping() {
        if (!elasticsearchRestTemplate.indexExists(ProductEs.class)) {
            IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(ProductEs.class);
            //创建索引
            boolean a = indexOperations.create();
            if (a) {
                //生成映射
                Document mapping = indexOperations.createMapping();
                //推送映射
                boolean b = indexOperations.putMapping(mapping);
                return b;
            } else {
                return a;
            }
        }
        return true;

    }
    public void db2es() throws Exception {
        long total = 0;
        long end = 0;
        long current = 1;
        long pages = 1;
        ProductReq req = new ProductReq();
        req.setSize(pageExportSize);

        while (current <= pages) {
            req.setCurrent(current);
            QueryWrapper queryWrapper = new QueryWrapper(req);
            com.baomidou.mybatisplus.extension.plugins.pagination.Page<Product> page = this.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(req.getCurrent(), req.getSize()), queryWrapper);
            total = page.getTotal();
            pages = page.getPages();
            current++;
            page2es(page);
        } ;
        log.debug("current: " + current);
        log.debug("pages: " + pages);

    }

    private void page2es(com.baomidou.mybatisplus.extension.plugins.pagination.Page<Product> page) throws Exception {
        BulkRequest bulkRequest = new BulkRequest();
        List<Product> list = page.getRecords();
        for (Product entity : list) {
            Long key = entity.getId();
            JSONConfig jsonConfig = new JSONConfig();
            jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
            String jsonValue = JSONUtil.toJsonStr(entity, jsonConfig);
            //可以是各种请求：crud
            IndexRequest indexRequest = new IndexRequest(ProductEs.getDocName());
            indexRequest.source(jsonValue, XContentType.JSON);
            indexRequest.id(key.toString());
            bulkRequest.add(indexRequest);
        }
        //设置超时
        bulkRequest.timeout("10s");
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (bulkResponse.status() != RestStatus.OK) {
            throw new Exception("es运行异常");
        }
    }

    public Page<ProductEs> multiMatch(@RequestParam("keyword") String keyword) throws Exception {
        check();
        SearchRequest searchRequest = new SearchRequest(ProductEs.getDocName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(
                        QueryBuilders.multiMatchQuery(keyword, ProductEs.getDocStringFieldName()).operator(Operator.OR)
                )
                .from(0)//起始条数
                .size(10)//每页展示记录
                .postFilter(QueryBuilders.matchAllQuery()) //过滤条件
                .sort("_id", SortOrder.DESC);//排序;
        //创建搜索请求
        searchRequest.source(searchSourceBuilder);
        /**
         * 打印dsl语句
         */
        log.debug("dsl:" + searchSourceBuilder.toString());
        long total = 0;
        List<ProductEs> list = new ArrayList<ProductEs>();
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        if (response.status() == RestStatus.OK) {
            total = response.getHits().getTotalHits().value;
            System.out.println("符合条件的文档总数: " + total);
            System.out.println("符合条件的文档最大得分: " + response.getHits().getMaxScore());
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                ProductEs e = JSONUtil.toBean(sourceAsString, ProductEs.class);
                list.add(e);
            }
        } else {
            throw new Exception("es运行异常");
        }

        return new PageImpl<ProductEs>(list, PageRequest.of(0, 10), total);

    }
    private void check() throws ParseException {
        String userId = loginService.getCurrentUser().getId();
        QueryWrapper queryWrapper = new QueryWrapper<Rule>();
        queryWrapper.orderBy(true,true, ColumnUtil.getFieldName(Rule::getFromTimestamp),ColumnUtil.getFieldName(Rule::getTimes));
        List<Rule> list = iRuleService.list(queryWrapper);

        for (Rule rule : list) {
            long epochMilli = LocalDateTimeUtils.getCurrentMilli();
            if(epochMilli>=rule.getFromTimestamp()&&epochMilli<=rule.getToTimestamp()&& Objects.equals(userId,rule.getUserId())){
                String k = RedisBusConst.USER_ID_REDIS_NAME_SPACE_PREFIX + rule.getFromTimestamp() + ":"  + rule.getToTimestamp() + ":" + userId;
                stringRedisTemplate.opsForValue().increment(k,rule.getToTimestamp());
                String v = stringRedisTemplate.opsForValue().get(k);
                Integer vi = Integer.valueOf(Integer.parseInt(v));
                if(vi>rule.getTimes()){
                    throw new RuntimeException(String.format("违反规则%s" , JSONUtil.toJsonStr(rule)));
                }
            }
        }


        String currentDayStr = LocalDateTimeUtils.getCurrentDayStr();
        String key = RedisBusConst.USER_ID_REDIS_NAME_SPACE_PREFIX + currentDayStr + ":" + userId;
        long nextDayStartLong = LocalDateTimeUtils.getNextDayStartLong();
        stringRedisTemplate.opsForValue().increment(key,nextDayStartLong);
        String value = stringRedisTemplate.opsForValue().get(key);
        Integer valuei = Integer.valueOf(Integer.parseInt(value));
        if(valuei>visitCountMax){
            throw new RuntimeException("经过身份验证的用户每天只能使用该接口100次。");
        }
    }



}
